package com.passion.solr.config;

import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.impl.LBHttpSolrClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import java.net.URI;
import java.util.Arrays;
import java.util.Optional;

/**
 * solr配置
 *
 * @author ZhuYin
 * @since 2023/06/19
 */
@ConditionalOnProperty(prefix = "archives.search", name = "type", havingValue = "solr")
@Configuration
@ConfigurationProperties(prefix = SolrConfig.PREFIX)
public class SolrConfig {

    public static final String PREFIX = "spring.data.solr";

    /**
     * 排序类型
     */
    public static final String DESC = SolrQuery.ORDER.desc.name();
    public static final String ASC = SolrQuery.ORDER.asc.name();

    /**
     * 初始页
     */
    public static final int MIN_PAGE_NUM = 1;

    /**
     * 页面最大值
     */
    public static final int MAX_PAGE_SIZE = 99999;
    /**
     * 页面最小值
     */
    public static final int MIN_PAGE_SIZE = 10;

    /**
     * 主键字段
     */
    public static final String SOLR_ID = "id";


    /** 默认集群模式关闭  */
    public static Boolean cluster;
    /** zk鉴权开关 */
    public static Boolean zkAuth;
    /** zk账号密码 */
    public static String zkUsername;
    public static String zkPassword;

    /** 默认关闭鉴权 */
    public static Boolean auth;

    public static String host;
    public static String zkHost;
    public static String username;
    public static String password;
    /** solr系统加密后的密码 */
    public static String enPassword;
    public static String defaultCore;

    public static String highLightPreTag;
    public static String highLightPostTag;

    public static Boolean logEnabled;


    @Value("${spring.data.solr.cluster:false}")
    public void setCluster(Boolean cluster) {
        SolrConfig.cluster = cluster;
    }

    @Value("${spring.data.solr.zkAuth:false}")
    public void setZkAuth(Boolean zkAuth) {
        SolrConfig.zkAuth = zkAuth;
    }
    @Value("${spring.data.solr.zkUsername:admin}")
    public void setZkUsername(String zkUsername) {
        SolrConfig.zkUsername = zkUsername;
    }
    @Value("${spring.data.solr.zkPassword:tlkj@admin123}")
    public void setZkPassword(String zkPassword) {
        SolrConfig.zkPassword = zkPassword;
    }

    @Value("${spring.data.solr.auth:false}")
    public void setAuth(Boolean auth) {
        SolrConfig.auth = auth;
    }

    @Value("${spring.data.solr.host:}")
    public void setHost(String host) {
        SolrConfig.host = host;
    }

    @Value("${spring.data.solr.zk-host:}")
    public void setZkHost(String zkHost) {
        SolrConfig.zkHost = zkHost;
    }

    @Value("${spring.data.solr.username:}")
    public void setUsername(String username) {
        SolrConfig.username = username;
    }

    @Value("${spring.data.solr.password:}")
    public void setPassword(String password) {
        SolrConfig.password = password;
    }

    @Value("${spring.data.solr.defaultCore:archives}")
    public void setDefaultCore(String defaultCore) {
        SolrConfig.defaultCore = defaultCore;
    }

    @Value("${spring.data.solr.highLightPreTag:<em>}")
    public void setSpringDataSolrHighLightPreTag(String highLightPreTag) {
        SolrConfig.highLightPreTag = highLightPreTag;
    }

    @Value("${spring.data.solr.highLightPostTag:</em>}")
    public void setSpringDataSolrHighLightPostTag(String highLightPostTag) {
        SolrConfig.highLightPostTag = highLightPostTag;
    }

    @Value("${spring.data.solr.enPassword:}")
    public void setEnPassword(String enPassword) {
        SolrConfig.enPassword = enPassword;
    }

    @Value("${spring.data.solr.logEnabled:false}")
    public void setLogEnabled(Boolean logEnabled) {
        SolrConfig.logEnabled = logEnabled;
    }

    @ConditionalOnBean(SolrConfig.class)
    @Lazy
    @Bean
    public SolrClient solrClient() {
        if (cluster) {
            // 集群版
            return solrCloudClient();
        } else {
            // 单机版
            return solrStandAlone();
        }
    }

    /**
     * 单机版 solr 客户端
     *
     * @return {@link HttpSolrClient }
     * @author ZhuYin
     * @since 2023/06/20
     */
    private HttpSolrClient solrStandAlone() {
        final URI uri = URI.create(host);
        HttpClientBuilder builder = HttpClientBuilder.create();
        // 如果solr开启了权限认证
        if (auth) {
            CredentialsProvider provider = new BasicCredentialsProvider();
            provider.setCredentials(new AuthScope(uri.getHost(), uri.getPort()), new UsernamePasswordCredentials(username, password));
            builder.setDefaultCredentialsProvider(provider);

            // 指定拦截器，用于设置认证信息
            builder.addInterceptorFirst(new SolrAuthInterceptor());
        }
        CloseableHttpClient httpClient = builder.build();
        return new HttpSolrClient.Builder(host).withHttpClient(httpClient).build();
    }

    /**
     * 集群版 solr 客户端
     *
     * @return {@link CloudSolrClient }
     * @author ZhuYin
     * @since 2023/06/20
     */
    private CloudSolrClient solrCloudClient() {
        CloudSolrClient.Builder builder = new CloudSolrClient.Builder(Arrays.asList(zkHost.split(",")), Optional.empty());
        builder.withConnectionTimeout(60000);

        CloudSolrClient solrClodClient;
        if (auth) {
            // 设置账号密码
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
            CredentialsProvider provider = new BasicCredentialsProvider();
            provider.setCredentials(AuthScope.ANY, credentials);

            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
            httpClientBuilder.setDefaultCredentialsProvider(provider);

            // 指定拦截器，用于设置认证信息
            httpClientBuilder.addInterceptorFirst(new SolrAuthInterceptor());

            CloseableHttpClient httpClient = httpClientBuilder.build();

            LBHttpSolrClient.Builder lbBuilder = new LBHttpSolrClient.Builder();
            lbBuilder.withHttpClient(httpClient);
            lbBuilder.withConnectionTimeout(60000);
            builder.withLBHttpSolrClient(lbBuilder.build());
        }
        solrClodClient = builder.build();
        solrClodClient.setDefaultCollection(defaultCore);
        return solrClodClient;
    }

    public static class SolrAuthInterceptor implements HttpRequestInterceptor {
        @Override
        public void process(final HttpRequest request, final HttpContext context) {
            AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
            if (authState.getAuthScheme() == null) {
                CredentialsProvider provider =
                        (CredentialsProvider) context.getAttribute(HttpClientContext.CREDS_PROVIDER);
                HttpHost httpHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
                AuthScope scope = new AuthScope(httpHost.getHostName(), httpHost.getPort());
                Credentials credentials = provider.getCredentials(scope);
                authState.update(new BasicScheme(), credentials);
            }
        }
    }

}

